﻿using System;
using GameClient.LobbyReference;
using GameClient.RoomReference;

using System.ServiceModel;
using frag_boardgame;
using WCFCommon;
using WCFConsoleUI;
using System.Collections.Generic;






namespace GameClient
{
    public enum UserShellState
    {
        Connect, Login, Lobby, Room, InGame, Quitting
    }
    class Program
    {
        static void Main(string[] args)
        {
            UserShell userShell = new UserShell();

            userShell.run();
        }

    }

    public class UserShell
    {
        #region private fields

        private LobbyClient lobbyClient;
        private RoomClient roomClient;
        private GameData gameData;

        private UserShellState state = UserShellState.Connect;
        private bool isCurrentPlayer = false;
        private bool isGuest = false;

        private string userName;

        
        private string roomName;
        private string serverAddress;
        private int serverPort = 2121;


       

        #endregion

        #region properties
        public string UserName
        {
            get { return userName; }
            set { userName = value; }
        }
       


        public string RoomName
        {
            get { return roomName; }
            set { roomName = value; }
        }

        public string ServerAddress
        {
            get { return serverAddress; }
            set { serverAddress = value; }
        }

        public int ServerPort
        {
            get { return serverPort; }
            set { serverPort = value; }
        }

        public UserShellState State
        {
            get { return state; }
        }
        
        #endregion

        # region game states handlers

        private void connectionHandler()
        {
            bool success = false;
            while (!success)
            {
                serverAddress = ConsoleUI.GetServerAddress();
                success = ConnectToServer();
                if (success == false)
                {
                    
                    ConsoleUI.Error("Can't connect to server " + serverAddress);
                }
            }
        }

        private void loginHandler()
        {
            string password = "";
            bool allParamsOk = false;
            while (!allParamsOk)
            {
                LoginOptions loginOption = ConsoleUI.GetLoginCommand();
                
                if (loginOption== LoginOptions.Exist)
                {
                    userName = ConsoleUI.GetName();
                    userName = userName.Split(' ', '\n')[0];
                    password = ConsoleUI.GetPassword();
                    
                    AuthenticationResult authentication_Res = LoginToServer_Authentication(password);
                    
                    if (authentication_Res == AuthenticationResult.IncorrectUserName)
                        ConsoleUI.Error("User name doesn't exist");
                    else if (authentication_Res == AuthenticationResult.IncorrectPassword)
                        ConsoleUI.Error("Incorrect password");
                    else if (authentication_Res == AuthenticationResult.UserAlreadyLoggedIn)
                        ConsoleUI.Error("User is already logged in");
                    else if (authentication_Res == AuthenticationResult.UnknownError)
                        ConsoleUI.Error("Login failed due to unknown reason");
                    else
                        allParamsOk = true;

                }
                else if (loginOption== LoginOptions.New)
                {
                    userName = ConsoleUI.GetName();
                    userName = userName.Split(' ', '\n')[0];
                    password = ConsoleUI.GetPassword();
                    
                    string confirmPassword = ConsoleUI.ConfirmPassword();
                    bool passwordsMatch = (password == confirmPassword);
                    while (!passwordsMatch)
                    {
                        ConsoleUI.Error("Passwords do not match, please try again");
                        password = ConsoleUI.GetPassword();
                        confirmPassword = ConsoleUI.ConfirmPassword();
                        passwordsMatch = (password == confirmPassword);
                    }
                    string email = ConsoleUI.GetEmail();
                    AddingNewUserResult addRes = lobbyClient.AddUser(userName,password,email);
                    if (addRes == AddingNewUserResult.UserExist)
                        ConsoleUI.Error("User already exists");
                    else if (addRes == AddingNewUserResult.IncorrectEmail)
                        ConsoleUI.Error("Email is invalid");
                    else if (addRes == AddingNewUserResult.UnknownError)
                        ConsoleUI.Error("Adding user is failed due to unknown reason");
                    else
                        allParamsOk = true;                       

                    
                }
                else if (loginOption == LoginOptions.Guest)
                {
                    if (LoginToServer_Guest())
                        allParamsOk = true;
                    else
                        ConsoleUI.Error("Login failed due to unknown reason");

        
                }

            }
            

           
            //Initilize lobby's menu:
            ConsoleUI.InitilzeLobbyMenu(userName);
            state = UserShellState.Lobby;

        }

        private void lobbyHandler()
        {
            ConsoleUI.DrawLobby();
            string room_name;
            LobbyOptions lobbyOption = ConsoleUI.GetLobbyCommand(out room_name);
           
            if (lobbyOption == LobbyOptions.PrintRooms)
            {
                PrintRooms();
            }
            else if (lobbyOption == LobbyOptions.Create)
            {
                lobbyClient.GetPresetBoardsList();
                ConsoleUI.PrintBoardsList(lobbyClient.GetPresetBoardsList());
                GameConfiguration config = ConsoleUI.GetConfiguration();
                CreateRoom(room_name,config);
            }
            else if (lobbyOption == LobbyOptions.Join)
            {
                JoinRoom(room_name);
            }
            else if (lobbyOption == LobbyOptions.Help)
            {

            }
            else if (lobbyOption == LobbyOptions.Quit)
            {
                QuitLobby();
                state = UserShellState.Quitting;
            }
            else if (lobbyOption == LobbyOptions.Score)
            {
                PrintScore();
            }
            else if (lobbyOption == LobbyOptions.History)
            {
                PrintHistory();
            }


            if (state == UserShellState.Room)
            {
               ConsoleUI.InitilzeRoomMenu(roomName);
            }

        }

        private void roomHandler()
        {
            ConsoleUI.DrawRoom();
           

            string message;
            RoomOptions command = ConsoleUI.GetRoomCommand(out message);
           
            if (command == RoomOptions.Say)
            {
                SayMessageLobby(message);
            }
            else if (command == RoomOptions.Start)
            {
                StartGame();
            }
            else if (command == RoomOptions.Quit)
            {
                QuitRoom();
            }
            else if (command == RoomOptions.PrintPlayers)
            {
                PrintPlayers();
            }
            else if (command == RoomOptions.Wait)
            {
                WaitForGame();
            }
        }

        private void gameHandler()
        {
            //while (!isCurrentPlayer || !(state == UserShellState.InGame)) ;
            while ((state == UserShellState.InGame) && (!isCurrentPlayer)) ;
            if (state == UserShellState.InGame)
            {
                Direction direction;
                int value;
                GameOptions command = ConsoleUI.GetGameCommand(out value, out direction);

                if (command == GameOptions.Move)
                {
                    MovePlayer(direction, value);
                }
                else if (command == GameOptions.Finish)
                {
                    FinishTurn();
                }
                else if (command == GameOptions.Shoot)
                {
                    ShootPlayer(value);
                }
                else if (command == GameOptions.Redo)
                {
                    Redo();
                }
                else if (command == GameOptions.Undo)
                {
                    Undo();
                }
            }

        }

        #endregion

        #region User Actions

        public void WaitForGame()
        {
            ConsoleUI.WriteLine("Waiting for a game to be started...");
            while (state != UserShellState.InGame) ;
            state = UserShellState.InGame;
        }

        public void StartGame()
        {
            roomClient.StartGame();
            state = UserShellState.InGame;
        }

        public void PrintPlayers()
        {
            Dictionary<string, int> users = roomClient.GetUsers();
           
            ConsoleUI.printPlayers(users);
        }
        
        public void CreateRoom(string name, GameConfiguration config)
        {
            try
            {
                lobbyClient.CreateRoom(name, config);
            }
            catch (System.ServiceModel.FaultException<RoomCreationFault> e)
            {
                ConsoleUI.Error(e.Detail.Problem);
                
            }

        }

        public void JoinRoom(string name)
        {
            
            InstanceContext context = new InstanceContext(new RoomCallback(this));
            roomClient = new RoomClient(context, "NetTcpBinding_IRoom", string.Format("net.tcp://{0}:{1}/WCFGameService/Room", serverAddress, serverPort));
            roomName = name;

            JoinRoomResult Res = roomClient.JoinRoom(roomName, userName);
            if (Res == JoinRoomResult.Succeed)
            {
                state = UserShellState.Room;
            }
            else
            {
                string message;
                if (Res == JoinRoomResult.RoomIsFull)
                    message = "Room is full";
                else if (Res == JoinRoomResult.RoomNotFound)
                    message = "Invalid room name";
                else if (Res == JoinRoomResult.GameStarted)
                    message = "Game is being played at room";
                else
                    message = "Unexpected error";
                ConsoleUI.Error(message);
                roomName = "";
                roomClient = null;
            }
        }

        public void QuitRoom()
        {
            roomClient.LeaveRoom();
            roomClient = null;
            state = UserShellState.Lobby;
            //inRoom = false;
        }

        public void PrintRooms()
        {
            
            RoomInfo[] rooms = lobbyClient.GetRooms();

            ConsoleUI.printRooms(rooms);

        }

        //Currently this method is being used only for the testings.
        //Probebly should be deleted, and tests should be changed.
        //Now I'll change the implementation of this method so the tests will succeed to build
        public bool LoginToServer_Guest()
        {
            bool success = false;
            try
            {
                string name = lobbyClient.Guest_Connection();
                this.userName = name;
                state = UserShellState.Lobby;                
                success = true;
                isGuest = true;
                
            }
            catch (System.ServiceModel.FaultException<UserCreationFault> e)
            {
                success = false;
            }
            return success;
        }

        public AddingNewUserResult LoginToServer_AddUser(string name,string password,string email)
        {
            AddingNewUserResult addRes = AddingNewUserResult.UnknownError;
            try
            {
                addRes = lobbyClient.AddUser(name, password, email);
                if (addRes == AddingNewUserResult.Succeed)
                {
                    state = UserShellState.Lobby;
                }
            }
            catch (System.ServiceModel.FaultException<UserCreationFault> e)
            {
                addRes = AddingNewUserResult.UnknownError;
            }
            return addRes;
        }

        public AuthenticationResult LoginToServer_Authentication(string password)
        {
            AuthenticationResult authenticationResult = AuthenticationResult.UnknownError;
            try
            {
                authenticationResult = lobbyClient.Authenticate(userName, password);
                if (authenticationResult == AuthenticationResult.Succeed)
                {
                    state = UserShellState.Lobby;
                }
            }
            catch (System.ServiceModel.FaultException<UserCreationFault> e)
            {
                authenticationResult = AuthenticationResult.UnknownError;
            }

            return authenticationResult;
        }

        public bool ConnectToServer()
        {
            bool success = false;
            try
            {


                InstanceContext context = new InstanceContext(new LobbyCallback());
                lobbyClient = new LobbyClient(context,"NetTcpBinding_ILobby", string.Format("net.tcp://{0}:{1}/WCFGameService/Lobby", serverAddress, serverPort));
                lobbyClient.InnerChannel.Open();
                state = UserShellState.Login;
                success = true;
            }
            catch (CommunicationException e)
            {
                Console.WriteLine(e.Message);
                success = false;

            }
            return success;
        }

        public void SayMessageLobby(string message)
        {
            roomClient.Say(message);
        }

        public void QuitLobby()
        {
            lobbyClient.Disconnect();
        }

        public void Undo()
        {
            roomClient.Undo();
        }

        public void Redo()
        {
            roomClient.Redo();
        }

        public void ShootPlayer(int value)
        {
            roomClient.Shoot(value);
        }

        public void FinishTurn()
        {
            isCurrentPlayer = false;
            roomClient.FinishCurrentTurn();
        }

        public void MovePlayer(Direction direction, int value)
        {
            roomClient.MovePlayer(value, direction);
        }

        public void PrintHistory()
        {
            ConsoleUI.PrintHistory( lobbyClient.GetPlayerHistory(userName) );
        }

        private void PrintScore()
        {
            if (isGuest)
                ConsoleUI.Error("Guests don't have a score!");
            else
            {
                int score = lobbyClient.GetPlayerScroe(userName);
                if (score > -1)
                    ConsoleUI.PrintScore(score);
                else
                    ConsoleUI.Error("Unknown error!");
            }
             
        }

        #endregion


        public void run()
        {

            state = UserShellState.Connect;

            while (state != UserShellState.Quitting)
            {
                
                switch (state)
                {
                    case UserShellState.Connect:
                        connectionHandler();
                        break;

                    case UserShellState.Login:
                        loginHandler();

                        break;

                    case UserShellState.Lobby:
                        
                        lobbyHandler();
                        break;

                    case UserShellState.Room:
                        
                        roomHandler();
                        break;

                    case UserShellState.InGame:
                       
                        gameHandler();
                        break;
                }
            }
        }




        public class RoomCallback : IRoomCallback
        {
            UserShell userShell;

            public RoomCallback(UserShell userShell)
            {
                this.userShell = userShell;
            }

            #region IRoomCallback Members

            public void ReceiveMessage(string sender, string message)
            {
                ConsoleUI.WriteLine(sender + " says: " + message);


            }

            public void UpdateData(GameData data, string message)
            {
                userShell.gameData = data;


                if (userShell.state != UserShellState.InGame)
                {
                    //userShell.gameData = true;
                    userShell.state = UserShellState.InGame;
                }

                ConsoleUI.SetLastLine(message);
                ConsoleUI.PrintValues(data);

            }

            public void SetCurrentPlayer()
            {
                userShell.isCurrentPlayer = !userShell.isCurrentPlayer;
            }

            public void GameOver(string message)
            {
                ConsoleUI.WriteLine(message);
                //userShell.inGame = false;
                userShell.state = UserShellState.Room;
            }

            #endregion
        }

        public class LobbyCallback : ILobbyCallback
        {

            #region ILobbyCallback Members

            public void CheckClient()
            {
                //Shouldn't do anything
            }

          

           


            public void Write(string text)
            {
                ConsoleUI.Error(text);
            }
            #endregion

        }

    }
}
